﻿// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.AttributedModel;
using System.ComponentModel.Composition.ReflectionModel;
using System.Reflection;
using Microsoft.Internal;

namespace System.ComponentModel.Composition.Caching.AttributedModel
{
    /// <summary>
    /// This cached catalog site is used by all catalogs that containe AttributedComposablePartDefinitions
    /// </summary>
    internal partial class AttributedComposablePartCatalogSite : ICachedComposablePartCatalogSite
    {
        private readonly Func<CachedAttributedComposablePartDefinition, ReflectionComposablePart> _partCreator;
        private readonly Action<IDictionary<string, object>, AttributedComposablePartDefinition> _partCreationInformationWriter;

        public AttributedComposablePartCatalogSite()
            : this(AttributedComposablePartCatalogSite.CreatePartFromCache, AttributedComposablePartCatalogSite.WritePartCreationInformation)
        {
        }

        public AttributedComposablePartCatalogSite(Func<CachedAttributedComposablePartDefinition, ReflectionComposablePart> partCreator, Action<IDictionary<string, object>, AttributedComposablePartDefinition> partCreationInformationWriter)
        {
            Assumes.NotNull(partCreator);
            Assumes.NotNull(partCreationInformationWriter);

            this._partCreator = partCreator;
            this._partCreationInformationWriter = partCreationInformationWriter;
        }

        public IDictionary<string, object> CacheExportDefinition(ExportDefinition exportDefinition)
        {
            AttributedMemberExportDefinition originalExport = exportDefinition as AttributedMemberExportDefinition;
            if (originalExport != null)
            {
                return CachedAttributedExportDefinition.CreateCache(originalExport);
            }

            CachedAttributedExportDefinition cachedExport = exportDefinition as CachedAttributedExportDefinition;
            Assumes.NotNull(cachedExport);
            return cachedExport.Cache;
        }

        public IDictionary<string, object> CacheImportDefinition(ImportDefinition importDefinition)
        {
            AttributedImportDefinition originalImport = importDefinition as AttributedImportDefinition;
            if (originalImport != null)
            {
                return CachedAttributedImportDefinition.CreateCache(originalImport);
            }

            CachedAttributedImportDefinition cachedImport = importDefinition as CachedAttributedImportDefinition;
            Assumes.NotNull(cachedImport);
            return cachedImport.Cache;
        }

        public IDictionary<string, object> CachePartDefinition(ComposablePartDefinition partDefinition)
        {
            AttributedComposablePartDefinition originalPartDefinition = partDefinition as AttributedComposablePartDefinition;
            if (originalPartDefinition != null)
            {
                IDictionary<string, object> cache = CachedAttributedComposablePartDefinition.CreateCache(originalPartDefinition);
                this._partCreationInformationWriter.Invoke(cache, originalPartDefinition);
                return cache;
            }

            CachedAttributedComposablePartDefinition cachedPartDefinition = partDefinition as CachedAttributedComposablePartDefinition;
            Assumes.NotNull(cachedPartDefinition);
            return cachedPartDefinition.Cache;
        }

        public ExportDefinition CreateExportDefinitionFromCache(IDictionary<string, object> cache)
        {
            return new CachedAttributedExportDefinition(cache);
        }

        public ImportDefinition CreateImportDefinitionFromCache(IDictionary<string, object> cache)
        {
            return new CachedAttributedImportDefinition(cache);
        }

        public ComposablePartDefinition CreatePartDefinitionFromCache(IDictionary<string, object> cache, Func<IEnumerable<ImportDefinition>> importsCreator, Func<IEnumerable<ExportDefinition>> exportsCreator)
        {
            return new CachedAttributedComposablePartDefinition(cache, this._partCreator, importsCreator, exportsCreator);
        }

        private static ReflectionComposablePart CreatePartFromCache(CachedAttributedComposablePartDefinition definition)
        {
            return new CachedAttributedComposablePart(definition);
        }

        private static AttributedComposablePartDefinition CreateAttributedPartDefinitionFromCache(CachedAttributedComposablePartDefinition definition)
        {
            return new AttributedComposablePartDefinition(definition.PartType);
        }

        private static void WritePartCreationInformation(IDictionary<string, object> cache, AttributedComposablePartDefinition partDefinition)
        {
            partDefinition.PartType.Assembly.CacheAssembly(cache);
        }

        internal static ComposablePartDefinition GetUpToDatePartDefinition(ComposablePartDefinition partDefinition)
        {
            // If this is already fully loaded definition, we are done
            AttributedComposablePartDefinition originalPartDefinition = partDefinition as AttributedComposablePartDefinition;
            if (originalPartDefinition != null)
            {
                return originalPartDefinition;
            }

            // if this is a cached definition, check its assembly cache
            CachedAttributedComposablePartDefinition cachedPartDefinition = partDefinition as CachedAttributedComposablePartDefinition;
            Assumes.NotNull(cachedPartDefinition);
            if (cachedPartDefinition.Cache.IsAssemblyCacheUpToDate())
            {
                return cachedPartDefinition;
            }
            else
            {
                // the assembly cache is not up-to-date, create the fully loaded one
                return AttributedComposablePartCatalogSite.CreateAttributedPartDefinitionFromCache(cachedPartDefinition);
            }
        }

    }
}
